home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / QuickDraw / OffSample / UFailure.p < prev   
Encoding:
Text File  |  1994-11-18  |  12.8 KB  |  349 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    Exception handling for MPW Pascal, MacApp and MPW C
  6. #
  7. #    UFailure (aka Signals) - “Exceptional code, with a few exceptions.”
  8. #
  9. #    UFailure.p    -    Pascal source
  10. #
  11. #    Copyright © 1985-1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.0                    11/88
  15. #
  16. #    Components:    UFailure.p            November 1, 1988
  17. #                UFailure.h            November 1, 1988
  18. #                UFailure.inc1.p        November 1, 1988
  19. #                UFailure.a            November 1, 1988
  20. #
  21. #    UFailure (or Signals) is a set of exception handling routines suitable for
  22. #    use with MacApp, MPW C, and MPW Pascal. It is a jazzed-up version of the MacApp
  23. #    UFailure unit. There is a set of C interfaces to it as well.
  24. #
  25. ------------------------------------------------------------------------------}
  26.  
  27.  
  28. {$P}
  29.  
  30. { We assume that MacApp is being used if qTrace is defined. IF not, you may use
  31.     “-mbg off” in the makefile if you don’t want TMONic symbology (i.e. embedded names). }
  32. {$IFC UNDEFINED qTrace}
  33.     {$SETC UsingMacApp := 0}
  34.     {$IFC UNDEFINED qDebug}
  35.         {$SETC qDebug := 0} {default to debugging code off without MacApp}
  36.     {$ENDC qDebug}
  37. {$ELSEC}
  38.     {$SETC UsingMacApp := 1}
  39. {$ENDC qTrace}
  40.  
  41. { Conditional compile flags used by this unit when used with MacApp:
  42.  
  43.     qDebug            True includes debugging code, false ignores debugging code.
  44.  
  45.     qTrace            True surrounds trap patches with $D+ and $D++ (because
  46.                     the default is $D++), false skips these directives.
  47.  
  48.     The settings of $D, $R and $N are imported from UMAUtil.
  49.  }
  50.  
  51. UNIT UFailure;
  52.  
  53. {
  54. Theory:
  55.     See the “theory of operation” comments below, and the commentary in Technical
  56.     Note #88.
  57.     
  58.     *** New ***
  59.     The warnings in technote 88 about not using CatchSignal in an expression, etc. no
  60.     longer apply. The exact state of the routine is now restored (all non-scratch
  61.     registers are preserved). Also, a fixed-size nonrelocatable block is now used, so
  62.     there is a limit on the depth of nested CatchSignals. This may be adjusted by
  63.     changing the constant SigBlockSize in UFailure.a and rebuilding the unit. For most
  64.     applications, though, the default depth of eight will be more than sufficient.
  65.     
  66.     Note: If you use CatchFailures to catch exceptions then you will still be required
  67.     to call Success or FreeSignal explicitly.
  68.     
  69.     *** You can use either the technote mechanism or the MacApp one in your program. It
  70.     is OK to call Signal if CatchFailures was used, and OK to call Failure if
  71.     CatchSignal was used, i.e. the two schemes may be freely intermixed. ***
  72.     
  73.     What this version adds to the MacApp mechanism is two things:
  74.         1.
  75.             Exception records (FailInfo) are taken from a special heap block when
  76.             you use CatchSignal, so you don’t have to pass a FailInfo record as a
  77.             parameter.
  78.         2.
  79.             You can take advantage of stack frames and use of Signals as described
  80.             in Technical Note #88, without being forced to make explicit calls to
  81.             FreeSignal. This unit is fully upwards compatible with the 1986 version
  82.             of the technote.
  83.             
  84.     This includes all of the MacApp calls from the original UFailure, of course.
  85.  
  86.                     T H E O R Y   O F   O P E R A T I O N
  87.  
  88.     This unit implements the MacApp and Signal failure mechanisms.
  89.  
  90.     The failure mechanism is built around exception handlers.  An exception
  91.     handler is a piece of code, generally local to some other routine, that is
  92.     called when a failure occurs and takes action to handle the failure.
  93.     An exception handler is of the form
  94.  
  95.         PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
  96.                                     or
  97.         the handler may consist of execution returning to the point of a
  98.         CatchSignal
  99.  
  100.     where error is the error that caused the failure, and message identifies
  101.     the error message that may be displayed.  Consider a routine that opens
  102.     a file, reads its contents, and closes the file.  If a failure occured
  103.     while reading the file, an exception handler would be needed to close the
  104.     file, as the rest of the routine would not be executed.  (See TestCignal
  105.     and TestSignal for examples of how to use these calls.)
  106.  
  107.     References to exception handlers are kept in the FailInfo record. The
  108.     exception handlers form a linked-list via the nextInfo field of FailInfo.
  109.     The linked list is a stack since new exception handlers are added to the
  110.     front of the list.
  111.  
  112.     New exception handlers are added to the stack with the CatchSignal
  113.     function or CatchFailures procedure. They are removed from the stack
  114.     automatically (CatchSignal) or via the Success procedure (if CatchFailures
  115.     was used). In general you call CatchFailures/CatchSignal to post an exception
  116.     handler when an error the application should handle might occur. You
  117.     can then manually pop the last handler off the stack with FreeSignal or
  118.     Success, if necessary. You may want to pop the handler (even if you used
  119.     CatchSignal) after the possibility of a specific type of error no longer
  120.     exists. Subsequent exceptions would then be passed to a previous (more
  121.     general) handler.
  122.     
  123.     Any failure detected within the limits of the CatchFailures/CatchSignal call
  124.     results in the execution of the exception handler.  (Failure does
  125.     not have to occur in the same routine as your call to CatchFailures.
  126.     The failure may occur in any routine called after the catch but before the
  127.     implicit/explicit pop of the handler.)
  128.  
  129.     When MacApp (or your code) determines that a failure has occured, it
  130.     calls Failure or Signal.  As a convenience, several procedures are provided
  131.     to check for standard kinds of failures and call Failure if needed.
  132.     These procedures are:
  133.  
  134.         FailNIL            Calls Failure if its parameter is NIL.
  135.         FailOSErr        Calls Failure if its parameter is not noErr.
  136.         FailMemError    Calls Failure if MemError returns other than noErr.
  137.         FailResError    Calls Failure if ResError returns other than noErr.
  138.  
  139.     When the exception is raised, execution of the routine that signalled is
  140.     terminated and the exception handler at the top of the stack is popped.
  141.     For each routine that was called after the handler was posted
  142.     to the stack, execution is terminated as though from an EXIT statement,
  143.     and the exception handler is called.  It generally cleans up for the
  144.     routine in which it is nested.  Upon completion the next exception handler
  145.     is popped from the stack, repeating the process.
  146.  
  147.     The error causing the failure, and a message code is passed to the handler.
  148.     
  149.     MacApp Specifics
  150.     For MacApp, the last exception handler on the stack is the one in
  151.     TApplication.PollEvent.  It calls TApplication.ShowError, which calls
  152.     ErrorAlert, which decodes the message and displays an alert.  Your exception
  153.     handlers may set the message code to one more specific to your application
  154.     by calling FailNewMessage at the end of your exception handler.
  155.     FailNewMessage changes the message only if the current one is non-zero.
  156.     This has the effect of allowing those exception handlers closest to the
  157.     source of the error to set the message.
  158.  
  159.     One last note about exception handlers:  It is possible for an exception
  160.     handler to terminate exception processing by using a non-local GOTO to
  161.     jump back into the routine in which the exception handler is nested.  This
  162.     is how MacApp keeps the application running when a failure occurs.  The
  163.     last exception handler on the stack, in TApplication.PollEvent, uses a
  164.     GOTO to continue event processing.
  165. }
  166.  
  167. INTERFACE
  168.  
  169. {$IFC UsingMacApp}
  170. USES
  171.     {$LOAD MacIntf.LOAD}
  172.         Types, QuickDraw, Events, Controls, Windows, TextEdit, Dialogs, Fonts, Lists,
  173.         Menus, Resources, Scrap, ToolUtils, 
  174.         OSUtils, Files, Devices, DeskBus, DiskInit, Disks, Errors, Memory,
  175.         Retrace, SegLoad, Serial,
  176.         ShutDown, Slots, Sound, Start, Timer, Packages,
  177.     {$LOAD}
  178.         UMAUtil
  179.     {$IFC qDebug}
  180.         , UWritelnWindow
  181.         , UTrace
  182.     {$ENDC}
  183.         ;
  184. {$ELSEC}
  185. USES
  186.     Types, QuickDraw, Events, Controls, Windows, TextEdit, Dialogs, Fonts, Lists,
  187.     Menus, Resources, Scrap, ToolUtils, 
  188.     OSUtils, Files, Devices, DeskBus, DiskInit, Disks, Errors, Memory, Retrace, SegLoad, Serial,
  189.     ShutDown, Slots, Sound, Start, Timer;
  190. {$ENDC UsingMacApp}
  191.  
  192.  
  193. {-----------------------------------+
  194. |    Global types                    |
  195. +-----------------------------------}
  196. TYPE
  197.         { used for the exception handling mechanism }
  198.     PFailInfo =        ^FailInfo;
  199.     FailInfo =        RECORD
  200.                     regs:            ARRAY[1..11] OF LONGINT;
  201.                     error:            INTEGER;
  202.                     message:        LONGINT;
  203.                     failA6:         LONGINT;
  204.                     failPC:         LONGINT;
  205.                     nextInfo:        PFailInfo;
  206.                     {this is needed for MacApp debugging}
  207.                     whoPC:            LONGINT;
  208.                     {these are added for USignalFailure unit use}
  209.                     whatSignals:    INTEGER;
  210.                     sigFRet:        LONGINT;    {used to keep the old stack frame return address}
  211.                     END;
  212.  
  213.  
  214. {-----------------------------------+
  215. |    Global variables                |
  216. +-----------------------------------}
  217. {$IFC UsingMacApp}
  218. {$IFC qDebug}
  219. VAR
  220.     {$PUSH}
  221.     {$Z+}    {make ’em accessable to assembly code}
  222.     gAskAboutAlloc:     BOOLEAN;    { if TRUE ask about each allocation to force
  223.                                         failure }
  224.     gAskFailure:        BOOLEAN;    { if TRUE ask about calls to
  225.                                         FailOSErr, FailResError, FailMemError,
  226.                                         and give user a chance to force error }
  227.     {$POP}
  228. {$ENDC qDebug}
  229. {$ENDC UsingMacApp}
  230.  
  231.  
  232. {-----------------------------------+
  233. |    Global routines                    |
  234. +-----------------------------------}
  235. {
  236. Call one of the two following initialization routines before your other
  237. initializations (InitGraf, etc.)--in other words as early as you can in the
  238. application.
  239. }
  240.  
  241. PROCEDURE InitUFailure;
  242.     { Allocates the heap block for CatchSignals and initializes the global
  243.         variables used by the unit. No CatchSignals from the main level
  244.         of Pascal are allowed if you use InitUFailure. [C programs must use this
  245.         instead of InitSignals.] }
  246.  
  247.  
  248. PROCEDURE InitSignals;
  249.     { Calls InitUFailure. It also sets up A6 at the main level of Pascal,
  250.         so it must be called from the outermost level of Pascal. }
  251.  
  252.  
  253. FUNCTION CatchSignal: INTEGER;
  254.     { Until the procedure which encloses this call returns, this will catch
  255.         subsequent Signal calls, returning the code passed to Signal.  When
  256.         CatchSignal is encountered initially, it returns a code of zero.  These
  257.         calls may "nest"; i.e. you may have multiple CatchSignals in one procedure.
  258.         If you signal with Failure and pass in a non-zero message you may want to use
  259.         CatchFailures instead so you have a way of getting at the message. }
  260.  
  261.  
  262. PROCEDURE FreeSignal;
  263.     { This undoes the effect of the last CatchSignal/CatchFailures.  A Signal will
  264.         then invoke the CatchSignal -prior- to the last one. }
  265.  
  266.  
  267. PROCEDURE Signal(code: INTEGER);
  268.     { Returns control to the point of the last CatchSignal/CatchFailures.  The program will
  269.         then behave as though that CatchSignal had returned with this code parameter.
  270.         If CatchFailures is catching, the message parameter will be 0. }
  271.  
  272.  
  273. PROCEDURE SignalMessage(code: INTEGER; message: LONGINT);
  274.     { Returns control to the point of the last CatchSignal/CatchFailures.
  275.         If CatchFailures is catching, the message parameter will be returned. }
  276.         
  277.         
  278. {-----------------------------------+
  279. |    MacApp routines                    |
  280. +-----------------------------------}
  281.  
  282.  
  283.  
  284. FUNCTION  BuildMessage(lowWord, highWord: INTEGER): LONGINT;
  285.             INLINE $2E9F;    { MOVE.L        (A7)+,(A7)     }
  286.     { Takes the 2 integers and combines them into a LONGINT.  Note that the
  287.         low-order word is the first parameter. }
  288.  
  289. PROCEDURE CatchFailures(VAR fi: FailInfo;
  290.                         PROCEDURE Handler(e: INTEGER; m: LONGINT));
  291.     { Call this to set up an exception handler.    This pushes your handler onto
  292.         a stack of exception handlers. }
  293.  
  294. PROCEDURE Failure(error: INTEGER; message: LONGINT);
  295.     { Call this to signal a failure.  Control will branch to the most recent
  296.         exception handler, which will be popped off the handler stack. }
  297.  
  298. PROCEDURE FailMemError;
  299.     { IF MemError <> noErr THEN Failure(MemError, 0);  If you are using
  300.         assembler, then you should just test the return code from the Memory
  301.         Manager in DO by calling FailOSErr.    (See the discussion of MemError in
  302.         Inside Macintosh.) }
  303.  
  304. PROCEDURE FailResError;
  305.     { IF ResError <> noErr THEN Failure(ResError, 0); (See Inside Macintosh.) }
  306.  
  307. PROCEDURE FailNewMessage(error: INTEGER; oldMessage, newMessage: LONGINT);
  308.     { This does:
  309.         IF oldMessage = 0 THEN
  310.             Failure(error, newMessage)
  311.         ELSE
  312.             Failure(error, oldMessage);
  313.      }
  314.  
  315. PROCEDURE FailNIL(p: UNIV Ptr);
  316.     { Call this with a pointer/handle; this signals Failure(memFullErr, 0) iff
  317.         the pointer is NIL. }
  318.  
  319. PROCEDURE FailOSErr(error: INTEGER);
  320.     { Call this with an OSError; signals Failure(error, 0) iff error <> noErr. }
  321.  
  322. {$IFC UsingMacApp}
  323. PROCEDURE SetInitHandler(handler: ProcPtr);
  324.     { Sets an initialization error handler; applications should not call
  325.         this, normally. }
  326. {$ENDC UsingMacApp}
  327.  
  328. PROCEDURE Success(VAR fi: FailInfo);
  329.     { Call this when you want to de-install your exception handler (pop 1
  330.         element off the handler stack). }
  331.         
  332. {$IFC UsingMacApp}
  333. {$IFC qDebug}
  334. PROCEDURE ProgramBreak(grievance: Str255);
  335.     { Causes a program break to the debug window, displaying grievance. }
  336.  
  337. PROCEDURE ProgramReport(grievance: Str255; break: BOOLEAN);
  338.     { Displays grievance in the debug window, and breaks if break is true. }
  339. {$ENDC qDebug}
  340. {$ENDC UsingMacApp}
  341.  
  342.  
  343.  
  344. IMPLEMENTATION
  345.  
  346. {$I UFailure.inc1.p}
  347.  
  348. END. {UFailure}
  349.